package data;


import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import java.util.TreeSet;

import logic.Encriptor;
import logic.Facade;

import xml.DataManipulate;

/**
 * container (or wrapper) for the data required for the movie recommendation system
 * @author the secret 4 and the dog
 *
 */
public class SystemXmlDataBase extends SystemData {
	//private
	static  SystemXmlDataBase _systemData;
	private Hashtable<String,MovieTuple> _movies;
	private Hashtable<String,UserTuple> _users;
	private Hashtable<String,RatingTuple> _ratings;

	/**
	 * creates a new instance of SystemData
	 * @param movies
	 * @param users
	 * @param recommendations
	 */
	public SystemXmlDataBase(Hashtable<String,MovieTuple> movies, Hashtable<String,UserTuple> users, Hashtable<String, RatingTuple> recommendations) {
		_movies = movies;
		_users = users;
		_ratings = recommendations;
	}

	public SystemXmlDataBase(){
		_movies = new Hashtable<String, MovieTuple>();
		_users = new Hashtable<String, UserTuple>();
		_ratings = new Hashtable<String, RatingTuple>();
	}

	public static void deleteSystemData(){
		_systemData=null;
	}
	
//	public static SystemXmlDataBase getInstance(){
//		if (_systemData==null)
//			return new SystemXmlDataBase();
//		else if (_systemData._movies==null)
//			return null;
//		else return _systemData;
//	}

//	public static SystemXmlDataBase init(Hashtable<String,MovieTuple> movies, Hashtable<String,UserTuple> users, Hashtable<String, RatingTuple> ratings) throws Exception {
//		if (_systemData==null) {
//			_systemData= new SystemXmlDataBase(movies,users,ratings);
//			return _systemData;
//		}
//		else throw new ObjectAlreadyInitializedException();//to do make a smart exception
//	}

	//public RatingTuple getRating(UserTuple user , MovieTuple movie)
	//{
	//	return _ratings.get(RatingTuple.calcKey(user,movie));
	//}

	public RatingTuple getRating(String user , String movie){
		return _ratings.get(user +"%" + movie);
	}

//	public void setRating(RatingTuple rating){
//		_ratings.put(rating.getKey(), rating);
//	}

//	public void removeRating(RatingTuple rating){
//		_ratings.remove(rating.getKey());
//	}
	//public Iterator <RatingTuple> getUserRatingsIterator(UserTuple user ){
	//	return _users.get(user.getKey()).getRatings().iterator();
	//	}
	//public Iterator<RatingTuple> getMovieRatingsIterator(MovieTuple movie){
	//	return _movies.get(movie.getKey()).getRatings().iterator();
	//	}
	//	public TreeSet<RatingTuple> getUserRatings(String user){
	//		//UserTuple t = _users.get(user);
	//		return _users.get(user).getRatings();
	//	}

	//	public TreeSet<RatingTuple> getMovieRatings(String movie){
	//		return _movies.get(movie).getRatings();
	//	}

/*
	/**
	 * gets an instance of systemData
	 */
	/*public static SystemData getInstance() throws NullPointerException{
		if (_systemData == null){
			throw new NullPointerException(); //someone unauthorized tried to get an instance
		}
		else{
			return _systemData;
		}
	}*/


	/**
	 *
	 * @return the movie
	 */
	public MovieTuple getMovie(String movie) {
		return _movies.get(movie);
	}

	/**
	 *
	 * @return the user
	 */
	public UserTuple getUser(String user) {
		return _users.get(user);
	}

/*	public boolean equals(SystemData other){
		boolean ans=true;
		if (!this._movies.equals(other._movies)){
		//	System.out.println("movies not equal");
			ans=false;
		}
		if (!this._ratings.equals(other._ratings)){

		//	System.out.println("rec not equal");
			ans=false;
		}
		if(!this._users.equals(other._users)){
		//	System.out.println("users not equal");
			ans=false;
		}
		//Collection otherCol = _
	//	System.out.println(ans);
		return ans;
	}*/
	@Deprecated
	public RatingTuple getRandomRec(){
		int rand = (int) (Math.random() * _ratings.size());
		return (RatingTuple) _ratings.values().toArray()[rand];
	}


	public void addMovie(MovieTuple movie){
		if (!_movies.containsKey(movie.getKey()))
			_movies.put(movie.getKey(), movie);
		//else return false;
		//return true;
	}

	public void addRating(String user , String movie, String rating , String description){
		MovieTuple movieTuple=getMovie(movie);
		UserTuple userTuple = SystemXmlDataBase.getInstance().getUser(user);
		RatingTuple ratingTuple = RatingTuple.createNew(movieTuple, userTuple, GregorianCalendar.getInstance().getTime().toString(), rating, description);
		if (!_movies.containsKey(ratingTuple.getKey()))
			_ratings.put(ratingTuple.getKey(), ratingTuple);
		else return;
		movieTuple.addRating(ratingTuple);
		userTuple.addRating(ratingTuple);
	}
	public void addUser(UserTuple user){
		if (!_users.containsKey(user.getKey()))
			_users.put(user.getKey(), user);
	}

//	public Hashtable<String, MovieTuple> get_movies() {
//		return _movies;
//	}
//	public void set_movies(Hashtable<String, MovieTuple> _movies) {
//		this._movies = _movies;
//	}
//
//	public Hashtable<String, UserTuple> get_users() {
//		return _users;
//	}
//
//	public void set_user(Hashtable<String, UserTuple> _user) {
//		this._users = _user;
//	}
//
//	public Hashtable<String, RatingTuple> get_rec() {
//		return _ratings;
//	}
//
//	public void set_rec(Hashtable<String, RatingTuple> _rec) {
//		this._ratings = _rec;
//	}

	public void deleteUser(UserTuple user) {
		deleteListOfRatings(user.getCopyOfRatings());
		_users.remove(user.getKey());
	}
	private void deleteListOfRatings(TreeSet<RatingTuple> ratings){
		for (RatingTuple rating : ratings){
    		RatingTuple rat = _ratings.get(rating.getKey());
    		this.deleteRating(rat);
    	}
	}
	public void deleteMovie(MovieTuple movie){

		deleteListOfRatings(movie.getCopyOfRatings());//;rating!=null; rating.next()){

		_movies.remove(movie.getKey());

	}

	private void deleteRating(RatingTuple rat){
		rat.get_movie().deleteRating(rat);
		rat.get_user().removeRating(rat);
   		_ratings.remove(rat.getKey());
	}
	//public void removeRating(String )

	public void deleteRating(String movieName, String userName) {
		deleteRating(this.getRating(userName, movieName));
	}

	public void editRating(String user, String movie, String description, String rate, String date) {
		
		RatingTuple r = getRating(user, movie);
		int oldUserRating = r.get_rating();
		r.get_movie().updateMovieRating(oldUserRating, Integer.parseInt(rate));
		r.edit(description, rate, date);
		
	}
	public void editUser(String name, String bdate, boolean sex){
		_users.get(name).editUser(bdate, sex);
		
	}
	public void changeUserPassword(String name, String password){
		try {
			String encryptedpass=Encriptor.encryptUserPassword(password);
			_users.get(name).changePassword(encryptedpass);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		
		
	}
	
	public UserTuple createUser(String name, String pw, String sex, String bdate, String type){
		String encryptedpass;
		try {
			encryptedpass = Encriptor.encryptUserPassword(pw);
			return new UserTuple(name, encryptedpass, sex, bdate, type);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}		
	}
	
	public TreeSet<RatingTuple> getRatingsByUser(String name){
		TreeSet<RatingTuple> ans=new TreeSet<RatingTuple>(new CompartorMovie());
		for (RatingTuple i: _ratings.values()){
			if (i.get_user().get_name()== name)
				ans.add(i);			
		}
		return ans;
	}
	
	public TreeSet<RatingTuple> getRatingsByMovie(String movie){
		TreeSet<RatingTuple> ans=new TreeSet<RatingTuple>(new CompartorUser());
		for (RatingTuple i: _ratings.values()){
			if (i.get_movie().get_name()== movie)
				ans.add(i);			
		}
		return ans;
	}

	@Override
	public Set<MovieTuple> getAllMovies() {
		return new HashSet<MovieTuple>(_movies.values());
	}

	@Override
	public Set<UserTuple> getAllUsers() {
		return new HashSet<UserTuple>(_users.values());
	}
	
	@Override
	public Set<RatingTuple> getAllRatings() {
		return new HashSet<RatingTuple>(_ratings.values());
	}
	
	@Override
	public void exit() {
		Collection<UserTuple> users = _users.values();
		Collection<MovieTuple> movies = _movies.values();
		Collection<RatingTuple> ratings = _ratings.values();
		DataManipulate.writeToFile(users, movies, ratings);
	}

	@Override
	public String[] getMovieNames() {
		return _movies.keySet().toArray(new String[_movies.size()]);
	}

	@Override
	public String[] getUserNames(String username) {
		Set<String> users2 = _users.keySet();
		String[] ans =new String[_users.size()-1];
		int i = 0;
		for (String s : users2){
			if (!s.equals(username)){
				ans[i] = s;
				i++;
			}
		}
		return ans;
	}
	
	public Hashtable<String , RatingTuple> get_rec(){
		return _ratings;
	}
	
	public String[] search(String str){
		String[] _allMovies=Facade.getInstance().getMovies();
		Arrays.sort(_allMovies);
		int count=0;
		String[] tmp;
		if(str.length()==1 && !Character.isDigit(str.charAt(0))){
			tmp = new String[_allMovies.length];
			for(int i=0; i<_allMovies.length; i++){
				if(_allMovies[i].toLowerCase().startsWith(str.toLowerCase())){
					tmp[count]=_allMovies[i];
					count++;				
				}
			}

		}
		else{
			String[] lc = new String[_allMovies.length];
			for(int i=0; i<_allMovies.length; i++){
				lc[i]=_allMovies[i].toLowerCase();
			}
			tmp = new String[_allMovies.length];
			for(int i=0; i<_allMovies.length; i++){
				if(lc[i].contains(str.toLowerCase())){
					tmp[count]=_allMovies[i];
					count++;				
				}
			}
		}
		String[] _searchMovies = new String[count];
		for(int i=0; i<count; i++){
			_searchMovies[i]=tmp[i];
		}
		return _searchMovies;
	}

	@Override
	public double getAvg(String user) {
		UserTuple u= _users.get(user);
		return u.getAvg();
	}
}
